package net.sunzc.demo.ui.activity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ListView;

import com.ta.TAApplication;
import com.ta.util.db.TASQLiteDatabase;
import com.ta.util.db.TASQLiteDatabasePool;

import net.sunzc.demo.R;
import net.sunzc.demo.listener.ITag;
import net.sunzc.demo.model.TestEntity;
import net.sunzc.demo.thread.DBThread;
import net.sunzc.demo.thread.TestThread;
import net.sunzc.demo.ui.adapter.DBAdapter;

import java.util.ArrayList;
import java.util.List;

/*
本测试内容：
1、在单一的线程中实现数据库读写操作
2、在不同的线程中分别实现数据库读写操作
结论：
1、数据库操作线程完全可以复用，无需每次都新建
2、数据库读和写放在一个线程中，HandlerThread会使用Loop的方式轮询逐次执行读和写，非常适合读和写关联的操作
3、数据库读写操作分别放在不同的线程中，这样会大大增加数据库操作的效率。每个操作之间互不影响。
4、使用线程操作数据库可以大大缓解UI线程的压力，使响应速度加快。
5、非常建议使用HandlerThread，这是一个全局可复用的线程，有自己的轮询机制与消息通知机制，Android系统的主线程机制与此非常相似（还未求证是否就是HandlerThread）。因此，完全可以新建一个线程做为WorkThread，用来全局使用。甚至可以根据工作类型的不同来继承HandlerThread创建出不同类型的工作线程。
6、完全可以基于HandlerThread来创建线程池
HandlerThread使用步骤：
1、新建一个HandlerThread对象
2、start方法开启
3、新建一个Handler
4、绑定looper
5、post runnable实现类
6、quit退出（退出后，本线程中还未执行的任务将不再执行）



你说说多线程操作数据库以下方法的优劣：
1、所有的数据库操作自始至终都只在一个工作线程中执行（这种方法的工作线程中所有的任务都是串联到一起，按照添加的先后顺序来执行的）
2、所有的数据库操作放到多个线程中执行，这些线程放到一个线程池中。（这种方法提高了数据库的操作效率，读写速度更快，但是每个操作之间的耦合度过松，数据交互麻烦，适合并发操作.!!!!这种方式是线程不安全的方法，因为两次写操作会出现不同的线程操作同一片数据表，引起数据混乱）
3、所有的写操作放到一个线程中，所有的读操作放到一个线程中（这种操作效率也很高，并且是线程安全的）
4、每个操作单独开一个线程

 */
public class DatabaseDemoActivity extends AppCompatActivity implements ITag {
    private TASQLiteDatabase mDatabase;
    private DBThread mDBThread, mDBThread1;//数据库操作线程
    private MainHandler mHandler;//用于从其他线程操作UI界面的Handler
    private Handler testHandler;//用于测试线程与其他线程交互用的
    private TestThread test;
    private TestRunnable testRunnable;
    private static int count = 0;
    private TASQLiteDatabasePool mDBPool;

    static class MainHandler extends Handler {
        public static final int WRITE_FINISH = 1;
        public static final int READ_FINISH = 0;


        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case WRITE_FINISH:
                    if (msg.arg1 % 200 == 0)
                        Log.i(DB_TAG, "写入成功，值为:" + msg.arg1);
                    break;
                case READ_FINISH:
                    adapter.notifyDataSetChanged((ArrayList<TestEntity>) msg.obj);
                    Log.i(DB_TAG, "第" + count++ + "刷新页面成功");
                    break;
            }
        }
    }

    private Handler mDBHandler, mDBHandler1;
    private static DBAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_database_demo);
        mHandler = new MainHandler();
        initView();
        initDB();
    }

    private void initView() {
        ListView dbListview = (ListView) findViewById(R.id.db_listview);
        ArrayList<TestEntity> mDatalist = new ArrayList<>();
        adapter = new DBAdapter(this, mDatalist);
        dbListview.setAdapter(adapter);
    }

    private void initDB() {
        mDBPool = ((TAApplication) getApplication()).getSQLiteDatabasePool();
        mDatabase = mDBPool.getSQLiteDatabase();
        if (mDatabase != null && mDatabase.hasTable(TestEntity.class)) {
            mDatabase.dropTable(TestEntity.class);
        } else {
            Log.i(DB_TAG, "获取数据库失败");
        }
        mDatabase.creatTable(TestEntity.class);

    }

    @Override
    protected void onDestroy() {
        mDBPool.releaseSQLiteDatabase(mDatabase);
        super.onDestroy();
    }

    private void initDBThread() {
        mDBThread = DBThread.getWriteThreadInstance();
        mDBThread.setPriority(Thread.MIN_PRIORITY);
        mDBThread.start();
        mDBHandler = new Handler(mDBThread.getLooper());

        //初始化第二条线程
        mDBThread1 = DBThread.getReadThreadInstance();
        mDBThread1.setPriority(Thread.MIN_PRIORITY);
        mDBThread1.start();
        mDBHandler1 = new Handler(mDBThread1.getLooper());
    }

    public void start(View view) {
        /*
        千万不可以在主线程频繁操作数据库，或者操作数据库的数据量过大，否则会严重影响主线程
        int index = 0;
        while (index <= 1000) {
            TestEntity testEntity = new TestEntity(index, index + 1, index + index + 1);
            if (mDatabase.insert(testEntity)) {
                Log.i(DB_TAG, "数据库插入成功" + index);
                if (index % 100 == 0) {
                    List<TestEntity> dblist = mDatabase.query(TestEntity.class, false, null, null, null, null, null);
                    adapter.notifyDataSetChanged((ArrayList<TestEntity>) dblist);
                    Log.i(DB_TAG, "第" + count++ + "次刷新页面成功");
                }
                index++;
            }
        }
        */
        initDBThread();
        test = new TestThread("Test1");
        test.setPriority(Thread.MIN_PRIORITY);
        test.start();
        testHandler = new Handler(test.getLooper());
        testRunnable = new TestRunnable();
        testHandler.post(testRunnable);
    }


    public void stop(View view) {
        mDBThread.quit();
        mDBThread1.quit();
        test.quit();
        Log.i(DB_TAG, "数据库读写线程停止,DB写线程状态:" + mDBThread.getState() + "读取线程状态为:" + mDBThread1.getState() + "测试线程状态" + test.getState());
    }

    class TestRunnable implements Runnable {

        private WriteRunnable write;
        private ReadRunnable read;
        public boolean isContinue = true;

        public TestRunnable() {

        }

        @Override
        public void run() {
            Log.i(DB_TAG, "测试线程开始工作,线程id为：" + Thread.currentThread().getId());
//            oneWay();//读写都放到一个线程中去
            twoWay();//读写分别在不同的线程
        }

        private void oneWay() {
            int index = 0;
            while (isContinue) {
                write.setIndex(index);
                mDBHandler.post(write);
                if (index % 100 == 0) {
                    mDBHandler.post(new ReadRunnable());
                }
                index++;
            }
        }

        private void twoWay() {
            int index = 0;
            while (index <= 1000) {
                write = new WriteRunnable();
                read = new ReadRunnable();
                ReadRunnable read1 = new ReadRunnable();
                write.setIndex(index);
                mDBHandler1.post(write);
//                mDBHandler.post(read);//不可多线程短时间读取数据库，容易出现数据库已经打开错误
                mDBHandler1.post(read1);
                Log.i(DB_TAG, "" + "index:" + index + "时刻为:" + System.currentTimeMillis());
//                mDBHandler.post(read);
                index++;
            }
        }
    }

    class ReadRunnable implements Runnable {

        @Override
        public void run() {
            //从数据库里面读出数据
            Log.i(DB_TAG, "读取线程为:" + Thread.currentThread().getId());
            TASQLiteDatabase db = mDBPool.getSQLiteDatabase();
            List<TestEntity> dblist = db.query(TestEntity.class, false, null, null, null, null, null);
            if (dblist != null && !dblist.isEmpty()) {
                Message msg = new Message();
                msg.what = MainHandler.READ_FINISH;
                msg.obj = dblist;
                mHandler.sendMessage(msg);
            }

        }
    }

    public class WriteRunnable implements Runnable {

        private int numA;
        private int numB;
        private int index;

        public WriteRunnable() {

        }

        @Override
        public void run() {
            //往数据库里面写入数据，或者叫插入数据
            Log.i(DB_TAG, "写入线程为:" + Thread.currentThread().getId() + "index为:" + index);
            TestEntity testEntity = new TestEntity(numA, numB, numA + numB);
            if (mDatabase.insert(testEntity)) {
                Message msg = new Message();
                msg.arg1 = numA;
                msg.what = MainHandler.WRITE_FINISH;
                mHandler.sendMessage(msg);
                Log.i(DB_TAG, "此次插入操作结束时间为" + System.currentTimeMillis() + "------index:" + index);
            }

        }

        public void setIndex(int index) {
            this.index = index;
            this.numA = index;
            this.numB = index + 1;
        }

        public int getIndex() {
            return index;
        }
    }
}

